package nzk.hssftemp;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Java reflect utility, mainly to get value from an object by field name only.
 *
 * @author N. Zulkarnaen (n.zulkarnaen@gmail.com)
 *         Date: 7/1/13
 *         Time: 5:19 PM
 * @version $Revision: 6 $
 */
public class ReflectUtil {

    /**
     * @param name field name
     * @param src  object to be read
     * @return field's value
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static Object getFieldValue(String name, Object src)
            throws NoSuchFieldException, IllegalAccessException {
        Class<?> cls = src.getClass();
        Field f = cls.getDeclaredField(name);
        f.setAccessible(true);
        return f.get(src);
    }

    public static String obj2String(Object o) {
        if (o instanceof Date) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            return sdf.format((Date) o);
        } else if (o instanceof Double) {
            DecimalFormat df = new DecimalFormat("0.#####");
            df.setGroupingSize(3);
            return df.format(o);
        } else if (o instanceof Integer) {
            return o.toString();
        } else {
            return o.toString();
        }
    }

    /**
     * get field value as string using predefined format.
     * supported data type : Double, Long, Date, others will be treated as String.
     *
     * @param name field name
     * @param src  object to be read
     * @return field value
     */
    public static String getFieldValueAsString(String name, Object src) {
        Object o;
        try {
            o = getFieldValue(name, src);
        } catch (Exception e) {
            e.printStackTrace();
            o = "-error-";
        }
        return obj2String(o);
    }

    /**
     * guess method name,
     * case:
     * id -> getId (normal),
     */
    static String guessGetter(String name) {
        return "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }

    /**
     * guess method name,
     * case:
     * active -> isActive (boolean),
     */
    static String guessGetterBoolean(String name) {
        return "is" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }

    /**
     * guess method name,
     * case:
     * URL -> getURL
     * iImage -> getiImage (one lowercase followed by uppercase).
     */
    static String guessGetterNoChange(String name) {
        return "get" + name;
    }

    /**
     * invoke getter method
     *
     * @param name field name ( method's name will be guessed)
     * @param src  object to be read
     * @return any object returned by invoked method.
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static Object invokeMethod(String name, Object src)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> cls = src.getClass();
        Method m = cls.getDeclaredMethod(guessGetter(name));
        if (m == null) {
            m = cls.getDeclaredMethod(guessGetterBoolean(name));
        }
        if (m == null) {
            m = cls.getDeclaredMethod(guessGetterNoChange(name));
        }
        if (m == null) {
            throw new IllegalAccessException("method not found for name '" + name + "'");
        } else {
            Object val = m.invoke(src);
            if (val != null) {
                return val;
            } else {
                return "-";
            }
        }
    }
}
